/*
-----------------------------------------------------------------------------
This source file is part of OGRE
(Object-oriented Graphics Rendering Engine)
For the latest info, see http://www.ogre3d.org/

Copyright (c) 2000-2007 The OGRE Team
Also see acknowledgements in Readme.html

You may use this sample code for anything you like, it is not covered by the
LGPL like the rest of the engine.
-----------------------------------------------------------------------------
*/

#include "stdafx.h"
#include "WallAvoid.h"
#include "Wall3D.h"
#include "Wall3DStrip.h"
#include "World.h"
//#include "MyCommon.h"
//#include "ParamLoader.h"

typedef Ogre::Vector3 OgreVec3;


bool WallAvoidFrameListener::frameStarted(const FrameEvent& evt)
{
	bool ret = ExampleFrameListener::frameStarted(evt);

	/*static double a(0);


	if(boid)
	{
		OgreVec3 pos = boid->Entity()->getParentNode()->getPosition();
		pos.y += 2*MyCommon::Math::Sin(a);
		boid->Entity()->getParentNode()->setPosition(pos);
	}

	a += MyCommon::Math::PI/100;
	if(a>MyCommon::Math::TWO_PI)
		a = -MyCommon::Math::TWO_PI;*/

	if (mWorld)
	{
		mWorld->Update(evt.timeSinceLastFrame);
	}

	return ret;
}

bool WallAvoidFrameListener::processUnbufferedKeyInput(const FrameEvent& evt)
{
	using namespace OIS;

	if(mKeyboard->isKeyDown(KC_B))
		mWorld->ToggleShowBBoxes(/*true*/);
	

	return ExampleFrameListener::processUnbufferedKeyInput(evt);
}


void WallAvoidApp::createCamera()
{
	// Create the camera
	mCamera = mSceneMgr->createCamera("PlayerCam");
	// Position it at 500 in Z direction
	mCamera->setPosition(Ogre::Vector3(0,170,-250));
	// Look back along -Z
	mCamera->lookAt(Ogre::Vector3(0,10,0));
	mCamera->setNearClipDistance(1);
}

//------------------------------------------------------------------------
//	create the mesh for a wall
//
//------------------------------------------------------------------------
void WallAvoidApp::createWallMesh(const Wall3D& wall,String strName)
{
	MyVec3 a( (wall.From()) );
	MyVec3 b( (wall.To()) );
	MyVec3 n( (wall.Normal()) );
	const size_t nVertices = 4;
	const size_t vbufCount = (3*2+2)*nVertices;
	float vertices[vbufCount] = 
	{
		a[0],a[1],a[2], n[0], n[1], n[2], 0, 0,
		b[0],b[1],b[2], n[0], n[1], n[2], 1, 0,
		b[0],b[1]+wall.Height(),b[2], n[0], n[1], n[2], 1, 1,
		a[0],a[1]+wall.Height(),a[2], n[0], n[1], n[2], 0, 1
	};

	// define 2 triangles
	const size_t ibufCount = 12;
	unsigned short faces[ibufCount] = 
	{
		0,1,2,
		0,2,3,
		1,0,3,
		1,3,2
	};

	MeshPtr wallmesh = MeshManager::getSingleton().createManual(strName,
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	SubMesh *pWallVertex = wallmesh->createSubMesh();

	wallmesh->sharedVertexData = new VertexData();
	VertexData* vertexData = wallmesh->sharedVertexData;

	// define the vertex format
	VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
	size_t currOffset = 0;
	// positions
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// normals
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// two dimensional texture coordinates
	vertexDecl->addElement(0, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
	currOffset += VertexElement::getTypeSize(VET_FLOAT2);

	// allocate the vertex buffer
	vertexData->vertexCount = 4;
	HardwareVertexBufferSharedPtr vBuf = HardwareBufferManager::getSingleton().
		createVertexBuffer(vertexDecl->getVertexSize(0),vertexData->vertexCount,
		HardwareBuffer::HBU_STATIC_WRITE_ONLY,false);
	/*VertexBufferBinding* binding = vertexData->vertexBufferBinding;
	binding->setBinding(0, vBuf);
	float* pVertex = static_cast<float*>(vBuf->lock(HardwareBuffer::HBL_DISCARD));*/
	// Upload the vertex data to the card
	vBuf->writeData(0,vBuf->getSizeInBytes(),vertices,true);

	/// Set vertex buffer binding so buffer 0 is bound to our vertex buffer
	VertexBufferBinding* bind = wallmesh->sharedVertexData->vertexBufferBinding; 
	bind->setBinding(0, vBuf);

	// allocate index buffer
	pWallVertex->indexData->indexCount = 12;
	pWallVertex->indexData->indexBuffer = HardwareBufferManager::getSingleton().
		createIndexBuffer(HardwareIndexBuffer::IT_16BIT,pWallVertex->indexData->indexCount,
			HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
	HardwareIndexBufferSharedPtr iBuf = pWallVertex->indexData->indexBuffer;
	/*unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(HardwareBuffer::HBL_DISCARD));*/
	iBuf->writeData(0,iBuf->getSizeInBytes(),faces,true);

	//// Set vertex buffer binding so buffer 1 is bound to our index buffer
	//Ogre::IndexBuffer ibind;
	//ibind->setBinding(1, iBuf);

	// Set parameters of the submesh
	pWallVertex->useSharedVertices = true;
	pWallVertex->indexData->indexStart = 0;

	// Set bounding information (for culling)
	wallmesh->_setBounds(Ogre::AxisAlignedBox(-25,-25,-25,25,25,25));
	wallmesh->_setBoundingSphereRadius(25);

	wallmesh->load();
}

//------------------------------------------------------------------------
//	
//
//------------------------------------------------------------------------
void WallAvoidApp::createWallMesh2(const Wall3D& wall,String strName)
{
	MeshPtr wallsMesh = MeshManager::getSingleton().createManual(strName,
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	SubMesh *wallsVertex = wallsMesh->createSubMesh();

	wallsMesh->sharedVertexData = new VertexData();
	VertexData* vertexData = wallsMesh->sharedVertexData;

	// define the vertex format
	VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
	size_t currOffset = 0;
	// positions
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// normals
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// two dimensional texture coordinates
	vertexDecl->addElement(0, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
	currOffset += VertexElement::getTypeSize(VET_FLOAT2);

	// allocate the vertex buffer
	vertexData->vertexCount = 4;
	HardwareVertexBufferSharedPtr vBuf = 
		HardwareBufferManager::getSingleton().createVertexBuffer(
		vertexDecl->getVertexSize(0),vertexData->vertexCount,
		HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY,false);
	VertexBufferBinding* binding = vertexData->vertexBufferBinding;
	binding->setBinding(0, vBuf);
	float* pVertex = static_cast<float*>(vBuf->lock(HardwareBuffer::HBL_DISCARD));

	// allocate index buffer
	wallsVertex->indexData->indexCount = 12;
	wallsVertex->indexData->indexBuffer = 
		HardwareBufferManager::getSingleton().createIndexBuffer(
		HardwareIndexBuffer::IT_16BIT, wallsVertex->indexData->indexCount,
		HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
	HardwareIndexBufferSharedPtr iBuf = wallsVertex->indexData->indexBuffer;
	unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(HardwareBuffer::HBL_DISCARD));

	MyReal height = wall.Height();
	MyVec3 curVec = wall.From();
	MyVec3 prevVec;
	int numWalls = 1;
	int vertIndex=0;


	// fill the first 2 vertices
	*pVertex++ = curVec[0];	
	*pVertex++ = curVec[1];	
	*pVertex++ = curVec[2];	

	// normal
	*pVertex++ = wall.Normal()[0];
	*pVertex++ = wall.Normal()[1];
	*pVertex++ = wall.Normal()[2];

	*pVertex++ = 0;
	*pVertex++ = 0;

	*pVertex++ = curVec[0];
	*pVertex++ = curVec[1]+height;
	*pVertex++ = curVec[2];

	// normal
	*pVertex++ = wall.Normal()[0];
	*pVertex++ = wall.Normal()[1];
	*pVertex++ = wall.Normal()[2];

	*pVertex++ = 0;
	*pVertex++ = 5;

	prevVec = curVec;

	for (int i=0; i<numWalls; i++)
	{
		//Wall3D wall = wstrip.Wall(i);
		curVec = wall.To();

		*pVertex++ = curVec[0];
		*pVertex++ = curVec[1];
		*pVertex++ = curVec[2];

		//normal
		*pVertex++ = wall.Normal()[0];
		*pVertex++ = wall.Normal()[1];
		*pVertex++ = wall.Normal()[2];

		// tex coords
		if(i%2==1)
		{
			*pVertex++ = 0;
			*pVertex++ = 0;
		}
		else
		{
			*pVertex++ = 1;
			*pVertex++ = 0;
		}

		*pVertex++ = curVec[0];
		*pVertex++ = curVec[1]+height;
		*pVertex++ = curVec[2];

		//normal
		*pVertex++ = wall.Normal()[0];
		*pVertex++ = wall.Normal()[1];
		*pVertex++ = wall.Normal()[2];

		// tex coords
		// tex coords
		if(i%2==1)
		{
			*pVertex++ = 0;
			*pVertex++ = 1;
		}
		else
		{
			*pVertex++ = 1;
			*pVertex++ = 5;
		}


		//indices -- each vertex is pointed by 3 indices
		*pIndices++ = vertIndex;
		*pIndices++ = vertIndex+2;
		*pIndices++ = vertIndex+3;

		*pIndices++ = vertIndex;
		*pIndices++ = vertIndex+3;
		*pIndices++ = vertIndex+1;

		*pIndices++ = vertIndex+2;
		*pIndices++ = vertIndex;
		*pIndices++ = vertIndex+1;

		*pIndices++ = vertIndex+2;
		*pIndices++ = vertIndex+1;
		*pIndices++ = vertIndex+3;

		vertIndex += 2;
	}

	// Unlock
	vBuf->unlock();
	iBuf->unlock();

	// Generate face list
	wallsVertex->useSharedVertices = true;
	wallsVertex->indexData->indexStart=0;

	// Set bounding information (for culling)
	wallsMesh->_setBounds(Ogre::AxisAlignedBox(-25,-25,-25,25,25,25));
	wallsMesh->_setBoundingSphereRadius(25);

	wallsMesh->load();


}

//------------------------------------------------------------------------
//	
//
//------------------------------------------------------------------------
void WallAvoidApp::createWallsMesh(const std::vector<Wall3D> walls,String strName)
{
	// if no walls, skip processing
	if(walls.empty())
		return;

	MeshPtr wallsMesh = MeshManager::getSingleton().createManual(strName,
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	SubMesh* wallsVertex = wallsMesh->createSubMesh();

	wallsMesh->sharedVertexData = new VertexData();
	VertexData* vertexData = wallsMesh->sharedVertexData;

	// define the vertex format
	VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
	size_t currOffset = 0;

	// positions
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// normals
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// two dimensional texture coordinates
	vertexDecl->addElement(0, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
	currOffset += VertexElement::getTypeSize(VET_FLOAT2);

	// allocate the vertex buffer
	vertexData->vertexCount = 4;

	// TODO: complete this
}

//------------------------------------------------------------------------
//	
//
//------------------------------------------------------------------------
void WallAvoidApp::createWallStripMesh(const Wall3DStrip* wstrip,String strName,
									   float htextile/*=0*/,float vtextile/*=0*/)
{
	MeshPtr wallsMesh = MeshManager::getSingleton().createManual(strName,
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	SubMesh *wallsVertex = wallsMesh->createSubMesh();

	wallsMesh->sharedVertexData = new VertexData();
	VertexData* vertexData = wallsMesh->sharedVertexData;

	// define the vertex format
	VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
	size_t currOffset = 0;
	// positions
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// normals
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// two dimensional texture coordinates
	vertexDecl->addElement(0, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
	currOffset += VertexElement::getTypeSize(VET_FLOAT2);

	// allocate the vertex buffer
	vertexData->vertexCount = 2*wstrip->NumWalls() + 2;
	HardwareVertexBufferSharedPtr vBuf = 
		HardwareBufferManager::getSingleton().createVertexBuffer(
			vertexDecl->getVertexSize(0),vertexData->vertexCount,
			HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY,false);
	VertexBufferBinding* binding = vertexData->vertexBufferBinding;
	binding->setBinding(0, vBuf);
	float* pVertex = static_cast<float*>(vBuf->lock(HardwareBuffer::HBL_DISCARD));

	// allocate index buffer
	wallsVertex->indexData->indexCount = 12*wstrip->NumWalls();
	wallsVertex->indexData->indexBuffer = 
		HardwareBufferManager::getSingleton().createIndexBuffer(
			HardwareIndexBuffer::IT_16BIT, wallsVertex->indexData->indexCount,
			HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
	HardwareIndexBufferSharedPtr iBuf = wallsVertex->indexData->indexBuffer;
	unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(HardwareBuffer::HBL_DISCARD));

	MyReal height = wstrip->WallsHeight();
	MyVec3 curVec = wstrip->Wall(0).From();
	MyVec3 prevVec;
	int numWalls = wstrip->NumWalls();
	int vertIndex=0;
	float prevhtex;
	float prevvtex;


	// fill the first 2 vertices
	*pVertex++ = curVec[0];	
	*pVertex++ = curVec[1];	
	*pVertex++ = curVec[2];	
	
	// normal
	*pVertex++ = wstrip->Wall(0).Normal()[0];
	*pVertex++ = wstrip->Wall(0).Normal()[1];
	*pVertex++ = wstrip->Wall(0).Normal()[2];

	*pVertex++ = 0;
	*pVertex++ = 0;

	*pVertex++ = curVec[0];
	*pVertex++ = curVec[1]+height;
	*pVertex++ = curVec[2];

	// normal
	*pVertex++ = wstrip->Wall(0).Normal()[0];
	*pVertex++ = wstrip->Wall(0).Normal()[1];
	*pVertex++ = wstrip->Wall(0).Normal()[2];

	*pVertex++ = 0;
	prevhtex = 0;
	prevvtex = (vtextile==0)?1:(float(height)/vtextile);
	*pVertex++ = prevvtex;
	
	/*if(vtextile==0)
		*pVertex++ = 1;
	else
		*pVertex++ = float(height)/vtextile;*/
	
	prevVec = curVec;

	for (int i=0; i<numWalls; i++)
	{
		Wall3D wall = wstrip->Wall(i);
		curVec = wall.To();
		
		*pVertex++ = curVec[0];
		*pVertex++ = curVec[1];
		*pVertex++ = curVec[2];

		//normal
		*pVertex++ = wall.Normal()[0];
		*pVertex++ = wall.Normal()[1];
		*pVertex++ = wall.Normal()[2];

		// tex coords
		//if(i%2==1)
		//{
		//	*pVertex++ = 0;
		//	*pVertex++ = 0;
		//}
		//else
		//{
		//	float xxx = (htextile==0)?1:(float((curVec-prevVec).length())/htextile);
		//	*pVertex++=xxx;
		//	/*if(htextile==0)
		//		*pVertex++ = 1;
		//	else
		//		*pVertex++ = float((curVec-prevVec).length())/htextile;*/
		//	*pVertex++ = 0;
		//}
		if (htextile==0)
		{
			*pVertex++ = ++prevhtex;
		}
		else
		{
			prevhtex += float((curVec-prevVec).length())/htextile;
			*pVertex++ = prevhtex;
		}
		*pVertex++ = 0;

		*pVertex++ = curVec[0];
		*pVertex++ = curVec[1]+height;
		*pVertex++ = curVec[2];

		//normal
		*pVertex++ = wall.Normal()[0];
		*pVertex++ = wall.Normal()[1];
		*pVertex++ = wall.Normal()[2];

		// tex coords
		//if(i%2==1)
		//{
		//	*pVertex++ = 0;
		//	float xxx = (vtextile==0)?1:(float(height)/vtextile);
		//	*pVertex++ = xxx;
		//	/*if(vtextile==0)
		//		*pVertex++ = 1;
		//	else
		//		*pVertex++ = float(height)/vtextile;*/
		//}
		//else
		//{
		//	//*pVertex++ = 1;
		//	float xxx = (htextile==0)?1 : (float((curVec-prevVec).length())/htextile);
		//	*pVertex++ = xxx;
		//	//*pVertex++ = 1;
		//	float yyy = (vtextile==0)?1 : (float(height)/vtextile);
		//	*pVertex++ = yyy;
		//}
		*pVertex++ = prevhtex;
		if (vtextile==0)
			*pVertex++ = 1;
		else
			*pVertex++ = prevvtex;
		

		//indices -- each vertex is pointed by 3 indices
		*pIndices++ = vertIndex;
		*pIndices++ = vertIndex+2;
		*pIndices++ = vertIndex+3;

		*pIndices++ = vertIndex;
		*pIndices++ = vertIndex+3;
		*pIndices++ = vertIndex+1;

		*pIndices++ = vertIndex+2;
		*pIndices++ = vertIndex;
		*pIndices++ = vertIndex+1;

		*pIndices++ = vertIndex+2;
		*pIndices++ = vertIndex+1;
		*pIndices++ = vertIndex+3;

		vertIndex += 2;
		prevVec = curVec;
	}

	// Unlock
	vBuf->unlock();
	iBuf->unlock();

	// Generate face list
	wallsVertex->useSharedVertices = true;
	wallsVertex->indexData->indexStart=0;

	wallsMesh->_setBounds( Ogre::AxisAlignedBox(-25,-25,-25,25,25,25));
	wallsMesh->_setBoundingSphereRadius(25);
	
	wallsMesh->load();
}


//------------------------------------------------------------------------
//	
//
//------------------------------------------------------------------------
void WallAvoidApp::createSphere(const std::string& strName, const float r, 
								const int nRings, const int nSegments)
{
	MeshPtr pSphere = MeshManager::getSingleton().createManual(strName, 
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	SubMesh *pSphereVertex = pSphere->createSubMesh();

	pSphere->sharedVertexData = new VertexData();
	VertexData* vertexData = pSphere->sharedVertexData;

	// define the vertex format
	VertexDeclaration* vertexDecl = vertexData->vertexDeclaration;
	size_t currOffset = 0;
	// positions
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_POSITION);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// normals
	vertexDecl->addElement(0, currOffset, VET_FLOAT3, VES_NORMAL);
	currOffset += VertexElement::getTypeSize(VET_FLOAT3);
	// two dimensional texture coordinates
	vertexDecl->addElement(0, currOffset, VET_FLOAT2, VES_TEXTURE_COORDINATES, 0);
	currOffset += VertexElement::getTypeSize(VET_FLOAT2);

	// allocate the vertex buffer
	vertexData->vertexCount = (nRings + 1) * (nSegments+1);
	HardwareVertexBufferSharedPtr vBuf = 
		HardwareBufferManager::getSingleton().createVertexBuffer(
		vertexDecl->getVertexSize(0), vertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
	VertexBufferBinding* binding = vertexData->vertexBufferBinding;
	binding->setBinding(0, vBuf);
	float* pVertex = static_cast<float*>(vBuf->lock(HardwareBuffer::HBL_DISCARD));

	// allocate index buffer
	pSphereVertex->indexData->indexCount = 6 * nRings * (nSegments + 1);
	pSphereVertex->indexData->indexBuffer = 
		HardwareBufferManager::getSingleton().createIndexBuffer(
		HardwareIndexBuffer::IT_16BIT, pSphereVertex->indexData->indexCount, 
		HardwareBuffer::HBU_STATIC_WRITE_ONLY, false);
	HardwareIndexBufferSharedPtr iBuf = pSphereVertex->indexData->indexBuffer;
	unsigned short* pIndices = static_cast<unsigned short*>(iBuf->lock(HardwareBuffer::HBL_DISCARD));

	float fDeltaRingAngle = (Ogre::Math::PI / nRings);
	float fDeltaSegAngle = (2 * Ogre::Math::PI / nSegments);
	unsigned short wVerticeIndex = 0 ;

	// Generate the group of rings for the sphere
	for( int ring = 0; ring <= nRings; ring++ ) {
		float r0 = r * sinf (ring * fDeltaRingAngle);
		float y0 = r * cosf (ring * fDeltaRingAngle);

		// Generate the group of segments for the current ring
		for(int seg = 0; seg <= nSegments; seg++) {
			float x0 = r0 * sinf(seg * fDeltaSegAngle);
			float z0 = r0 * cosf(seg * fDeltaSegAngle);

			// Add one vertex to the strip which makes up the sphere
			*pVertex++ = x0;
			*pVertex++ = y0;
			*pVertex++ = z0;

			OgreVec3 vNormal = OgreVec3(x0, y0, z0).normalisedCopy();
			*pVertex++ = vNormal.x;
			*pVertex++ = vNormal.y;
			*pVertex++ = vNormal.z;

			*pVertex++ = (float) seg / (float) nSegments;
			*pVertex++ = (float) ring / (float) nRings;

			if (ring != nRings) {
				// each vertex (except the last) has six indices pointing to it
				*pIndices++ = wVerticeIndex + nSegments + 1;
				*pIndices++ = wVerticeIndex;               
				*pIndices++ = wVerticeIndex + nSegments;
				*pIndices++ = wVerticeIndex + nSegments + 1;
				*pIndices++ = wVerticeIndex + 1;
				*pIndices++ = wVerticeIndex;
				wVerticeIndex ++;
			}
		}; // end for seg
	} // end for ring

	// Unlock
	vBuf->unlock();
	iBuf->unlock();
	// Generate face list
	pSphereVertex->useSharedVertices = true;

	// the original code was missing this line:
	pSphere->_setBounds( Ogre::AxisAlignedBox( OgreVec3(-r, -r, -r), OgreVec3(r, r, r) ), false );
	pSphere->_setBoundingSphereRadius(r);
	// this line makes clear the mesh is loaded (avoids memory leaks)
	pSphere->load();
}

//------------------------------------------------------------------------
//	just create a cube mesh
//
//------------------------------------------------------------------------
void WallAvoidApp::createCube()
{
	// Create the mesh via the MeshManager
	Ogre::MeshPtr msh = MeshManager::getSingleton().createManual("ColourCube","General");

	// Create one submesh
	SubMesh* sub = msh->createSubMesh();

	const float sqrt13 = 0.577350269f; /* sqrt(1/3) */

	// Define the vertices (8 vertices, each consisting of 2 groups of 3 floats
	const size_t nVertices = 8;
	const size_t vbufCount = 3*2*nVertices;
	float vertices[vbufCount] = {
		-10.0,10.0,-10.0,        //0 position
		-sqrt13,sqrt13,-sqrt13,     //0 normal
		10.0,10.0,-10.0,         //1 position
		sqrt13,sqrt13,-sqrt13,      //1 normal
		10.0,-10.0,-10.0,        //2 position
		sqrt13,-sqrt13,-sqrt13,     //2 normal
		-10.0,-10.0,-10.0,       //3 position
		-sqrt13,-sqrt13,-sqrt13,    //3 normal
		-10.0,10.0,10.0,         //4 position
		-sqrt13,sqrt13,sqrt13,      //4 normal
		10.0,10.0,10.0,          //5 position
		sqrt13,sqrt13,sqrt13,       //5 normal
		10.0,-10.0,10.0,         //6 position
		sqrt13,-sqrt13,sqrt13,      //6 normal
		-10.0,-10.0,10.0,        //7 position
		-sqrt13,-sqrt13,sqrt13,     //7 normal
	};

	RenderSystem* rs = Root::getSingleton().getRenderSystem();
	RGBA colours[nVertices];
	RGBA *pColour = colours;

	// Use render system to convert colour value since colour packing varies
	rs->convertColourValue(ColourValue(1.0,0.0,0.0), pColour++); //0 colour
	rs->convertColourValue(ColourValue(1.0,1.0,0.0), pColour++); //1 colour
	rs->convertColourValue(ColourValue(0.0,1.0,0.0), pColour++); //2 colour
	rs->convertColourValue(ColourValue(0.0,0.0,0.0), pColour++); //3 colour
	rs->convertColourValue(ColourValue(1.0,0.0,1.0), pColour++); //4 colour
	rs->convertColourValue(ColourValue(1.0,1.0,1.0), pColour++); //5 colour
	rs->convertColourValue(ColourValue(0.0,1.0,1.0), pColour++); //6 colour
	rs->convertColourValue(ColourValue(0.0,0.0,1.0), pColour++); //7 colour

	/// Define 12 triangles (two triangles per cube face)
	/// The values in this table refer to vertices in the above table
	const size_t ibufCount = 36;
	unsigned short faces[ibufCount] = {
		0,2,3,
		0,1,2,
		1,6,2,
		1,5,6,
		4,6,5,
		4,7,6,
		0,7,4,
		0,3,7,
		0,5,1,
		0,4,5,
		2,7,3,
		2,6,7
	};

	/// Create vertex data structure for 8 vertices shared between submeshes
	msh->sharedVertexData = new VertexData();
	msh->sharedVertexData->vertexCount = nVertices;

	/// Create declaration (memory format) of vertex data
	VertexDeclaration* decl = msh->sharedVertexData->vertexDeclaration;
	size_t offset = 0;

	// 1st buffer
	decl->addElement(0, offset, VET_FLOAT3, VES_POSITION);
	offset += VertexElement::getTypeSize(VET_FLOAT3);
	decl->addElement(0, offset, VET_FLOAT3, VES_NORMAL);
	offset += VertexElement::getTypeSize(VET_FLOAT3);

	/// Allocate vertex buffer of the requested number of vertices (vertexCount) 
	/// and bytes per vertex (offset)
	HardwareVertexBufferSharedPtr vbuf = 
		HardwareBufferManager::getSingleton().createVertexBuffer(
		offset, msh->sharedVertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
	/// Upload the vertex data to the card
	vbuf->writeData(0, vbuf->getSizeInBytes(), vertices, true);

	/// Set vertex buffer binding so buffer 0 is bound to our vertex buffer
	VertexBufferBinding* bind = msh->sharedVertexData->vertexBufferBinding; 
	bind->setBinding(0, vbuf);

	// 2nd buffer
	offset = 0;
	decl->addElement(1, offset, VET_COLOUR, VES_DIFFUSE);
	offset += VertexElement::getTypeSize(VET_COLOUR);
	/// Allocate vertex buffer of the requested number of vertices (vertexCount) 
	/// and bytes per vertex (offset)
	vbuf = HardwareBufferManager::getSingleton().createVertexBuffer(
		offset, msh->sharedVertexData->vertexCount, HardwareBuffer::HBU_STATIC_WRITE_ONLY);
	/// Upload the vertex data to the card
	vbuf->writeData(0, vbuf->getSizeInBytes(), colours, true);

	/// Set vertex buffer binding so buffer 1 is bound to our colour buffer
	bind->setBinding(1, vbuf);

	/// Allocate index buffer of the requested number of vertices (ibufCount) 
	HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager::getSingleton().
		createIndexBuffer(
		HardwareIndexBuffer::IT_16BIT, 
		ibufCount, 
		HardwareBuffer::HBU_STATIC_WRITE_ONLY);

	/// Upload the index data to the card
	ibuf->writeData(0, ibuf->getSizeInBytes(), faces, true);

	/// Set parameters of the submesh
	sub->useSharedVertices = true;
	sub->indexData->indexBuffer = ibuf;
	sub->indexData->indexCount = ibufCount;
	sub->indexData->indexStart = 0;

	/// Set bounding information (for culling)
	msh->_setBounds(Ogre::AxisAlignedBox(-100,-100,-100,100,100,100));
	msh->_setBoundingSphereRadius(Ogre::Math::Sqrt(3*100*100));

	/// Notify Mesh object that it has been loaded
	msh->load();
}

//------------------------------------------------------------------------
//	Just override the mandatory create scene method
//
//------------------------------------------------------------------------
void WallAvoidApp::createScene()
{
	//////////////////////////////////////////////////////////////////////////
	// TEST
	/*Wall3D wall(MyVec3(-10,0,0),MyVec3(10,0,0),20.0);
	createWallMesh2(wall,"mywall");

	Entity* wallEnt = mSceneMgr->createEntity("thewall","mywall");
	wallEnt->setMaterialName("Examples/Rockwall");
	wallEnt->setCastShadows(true);
	mSceneMgr->getRootSceneNode()->createChildSceneNode(OgreVec3(0,0,-20))->attachObject(wallEnt);*/
	
	/*createCube();
	Entity* thisEntity = mSceneMgr->createEntity("cc", "ColourCube");
	thisEntity->setMaterialName("Examples/Rockwall");
	SceneNode* thisSceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	thisSceneNode->setPosition(-35, 0, 0);
	thisSceneNode->attachObject(thisEntity);*/

	/*Wall3DStrip wallstrip(20);
	float ratio = -1.1f;
	int x=-20,z=20;
	for(int i=0; i<50; i++)
	{
		wallstrip.AddPoint(MyVec3(x,0,z));
		if (i%2==0)
			x *= ratio;
		else
			z *= ratio;
	}*/
	/*wallstrip.AddPoint(MyVec3(-20,0,20));
	wallstrip.AddPoint(MyVec3( 20,0,20));
	wallstrip.AddPoint(MyVec3( 5,0,-10));
	wallstrip.AddPoint(MyVec3( -10,0,-10));
	wallstrip.AddPoint(MyVec3( -10,0,20));
	wallstrip.AddPoint(MyVec3( 30,0,20));
	wallstrip.AddPoint(MyVec3( 30,0,-40));*/
	mWorld = new World(150,150,mSceneMgr);
	mWorld->NormalizeAgents(20);
	mWorld->ShowBoidBBox(true);

	//
	// create some walls
	createWallStripMesh(mWorld->Walls(),"wallstripmesh",20);
	//createWallStripMesh(&wallstrip,"wallstripmesh",20);
	Entity* wallstripEnt = mSceneMgr->createEntity("thewallstrip","wallstripmesh");
	wallstripEnt->setMaterialName("Examples/Rockwall");
	wallstripEnt->setCastShadows(true);
	mSceneMgr->getRootSceneNode()->createChildSceneNode(OgreVec3(0,0,0))->attachObject(wallstripEnt);

	//createSphere("mySphereMesh", 10, 64, 64);
	////Entity* sphereEntity = mSceneMgr->createEntity("mySphereEntity", "mySphereMesh");
	//sphereBoid = new Boid("mySphereEntity", "mySphereMesh",mSceneMgr);
	//SceneNode* sphereNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	////sphereEntity->setMaterialName("Examples/EnvMappedRustySteel");
	//sphereBoid->Entity()->setMaterialName("Examples/EnvMappedRustySteel");
	//sphereNode->attachObject(sphereBoid->Entity());
	

	//END TEST
	//////////////////////////////////////////////////////////////////////////

	
	Ogre::Plane floor;
	floor.normal = OgreVec3::UNIT_Y;
	floor.d = 100;
	MeshManager::getSingleton().createPlane("Floor",
		ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,floor,
		1500,1500,20,20,true,1,60,60,OgreVec3::UNIT_Z);
	Entity* floorEnt = mSceneMgr->createEntity("FloorEnt","Floor");
	floorEnt->setMaterialName("Examples/Rocky");
	floorEnt->setCastShadows(false);
	mSceneMgr->getRootSceneNode()->createChildSceneNode(OgreVec3(0,99,0))->attachObject(floorEnt);

	// Set ambient light
	mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

	// Create a light
	Light* l = mSceneMgr->createLight("MainLight");
	l->setPosition(20,80,50);
}

//------------------------------------------------------------------------
// Create new frame listener
//
//------------------------------------------------------------------------
void WallAvoidApp::createFrameListener()
{
	mFrameListener= new WallAvoidFrameListener(mSceneMgr, mWindow, mCamera,mWorld);
	mRoot->addFrameListener(mFrameListener);
}

//------------------------------------------------------------------------
//	destructor
//
//------------------------------------------------------------------------
WallAvoidApp::~WallAvoidApp()
{
	if (mWorld)
	{
		delete mWorld;
		mWorld=NULL;
	}
}




#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#endif

#ifdef __cplusplus
	extern "C" {
#endif

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
		INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
#else
		int main(int argc, char *argv[])
#endif
		{
			// Create application object
			WallAvoidApp app;

			try {
				app.go();
			} catch( Ogre::Exception& e ) {
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
				MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
#else
				std::cerr << "An exception has occured: " <<
					e.getFullDescription().c_str() << std::endl;
#endif
			}

			return 0;
		}

#ifdef __cplusplus
	}
#endif
